import os
import re
import sys
import shutil
import time
import unittest
from copy import deepcopy

# _BASE_DIR is the root directory of the tool and needs to be added to sys.path.
_BASE_DIR = os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.append(_BASE_DIR)

from framework.config_parser import ConfigFileParser
from framework.options_combiner import OptionCombiner
from framework.report.var_report import JSONFileKey as Key
from framework.report.json_maker import JsonMaker

MACHINE_INFO = {
    'product_model': {
        'command': 'dmidecode -s system-product-name',
        'result': 'KVM Virtual Machine'
    },
    'bios_version': {
        'command': 'dmidecode -s bios-version',
        'result': '0.0.0'
    },
    'arch': {
        'command': 'uname -m',
        'result': 'aarch64'
    },
    'kernel': {
        'command': 'uname -r',
        'result': '4.14.0-115.el7a.0.1.aarch64'
    },
    'cmdline': {
        'command': 'cat /proc/cmdline',
        'result': 'BOOT_IMAGE=/vmlinuz-4.14.0-115.el7a.0.1.aarch64 root=UUID=e2373560-d2f2-4281-8bcc-d8d8299aeec6 '
                  'ro crashkernel=auto LANG=en_US.UTF-8'
    },
    'page_size': {
        'command': 'getconf PAGE_SIZE',
        'result': '65536'
    },
    'gcc': {
        'command': 'gcc -dumpversion',
        'result': '4.8.5'
    },
    'gxx': {
        'command': 'g++ -dumpversion',
        'result': '4.8.5'
    },
    'python2': {
        'command': 'python2 --version',
        'result': 'Python 2.7.5'
    },
    'python3': {
        'command': 'python3 --version',
        'result': 'Python 3.7.0'
    },
    'java': {
        'command': 'java -version',
        'result': 'openjdk version "11-ea" 2018-09-25\nOpenJDK Runtime Environment (build 11-ea+28)\n'
                  'OpenJDK 64-Bit Server VM (build 11-ea+28, mixed mode, sharing)'
    },
    'glibc': {
        'command': 'ldd --version',
        'result': 'ldd (GNU libc) 2.17\nCopyright (C) 2012 Free Software Foundation, Inc.\n'
                  'This is free software; see the source for copying conditions.  There is NO\n'
                  'warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n'
                  'Written by Roland McGrath and Ulrich Drepper.'
    },
    'l1d_cache': {
        'command': 'lscpu | grep \'^L1d\' | grep -v "unknown"',
        'result': 'L1d cache:             64K'
    },
    'l1i_cache': {
        'command': 'lscpu | grep \'^L1i\' | grep -v "unknown"',
        'result': 'L1i cache:             64K'
    },
    'l2_cache': {
        'command': 'lscpu | grep \'^L2\' | grep -v "unknown"',
        'result': 'L2 cache:              512K'
    },
    'l3_cache': {
        'command': 'lscpu | grep \'^L3\' | grep -v "unknown"',
        'result': 'L3 cache:              32768K'
    },
    'cpu_model': {
        'command': "dmidecode -t 4 | grep Version | awk -F ':' '{print $2}' | uniq",
        'result': 'virt-4.0'
    },
    'cpu_nominal_mhz': {
        'command': 'dmidecode -t 4 | grep "Current Speed" | awk -F \':\' \'{print $2}\' | uniq',
        'result': '2000 MHz'
    },
    'cpu_max_mhz': {
        'command': 'dmidecode -t 4 | grep "Max Speed" | awk -F \':\' \'{print $2}\' | uniq',
        'result': '2000 MHz'
    },
    'cpu_bogomips': {
        'command': "lscpu | grep BogoMIPS | awk -F ':' '{print $2}'",
        'result': '200.00'
    },
    'cpu_logic_cores': {
        'command': 'cat /proc/cpuinfo | grep processor | wc -l',
        'result': '4'
    },
    'sockets': {
        'command': "lscpu | grep Socket | awk -F ':' '{print $2}'",
        'result': '4'
    },
    'cpu_cores': {
        'command': "lscpu | grep Core | awk -F ':' '{print $2}'",
        'result': '1'
    },
    'numa_topology': {
        'command': 'lscpu | grep NUMA',
        'result': 'NUMA node(s):          1\nNUMA node0 CPU(s):     0-3'
    },
    'memory_total': {
        'command': "cat /proc/meminfo | grep MemTotal | awk -F ':' '{print $2}'",
        'result': '7756928 kB'
    },
    'memory_count': {
        'command': 'dmidecode 2 | grep -A16 "Memory Device$" | grep "Size" | grep -v "No Module" | wc -l',
        'result': '1'
    },
    'memory_capacity': {
        'command': 'dmidecode 2 | grep -A16 "Memory Device$" | grep "Size" | grep -v "No Module Installed" '
                   '| grep -v "Unknown" | awk -F \':\' \'{print $2" |"}\' | xargs | sed "s/|$//"',
        'result': '8192 MB'
    },
    'memory_manu': {
        'command': 'dmidecode 2 | grep -A16 "Memory Device$" | grep "Manufacturer" | grep -v "Not Specified" |'
                   ' grep -v "Unknown" | grep -v "NO DIMM" | awk -F \':\' \'{print $2" |"}\' | xargs | sed "s/|$//"',
        'result': 'QEMU'
    },
    'memory_type': {
        'command': 'dmidecode 2 | grep -A16 "Memory Device$" | grep "Type:" | grep -v "Unknown" | grep -v "Other" |'
                   ' awk -F \':\' \'{print $2" |"}\' | xargs | sed "s/|$//"',
        'result': 'RAM'
    },
    'memory_speed': {
        'command': 'dmidecode 2 | grep -A16 "Memory Device$" | grep "Speed:" | grep -v "Unknown" |'
                   ' awk -F \':\' \'{print $2" |"}\' | xargs | sed "s/|$//"',
        'result': ''
    },
    'os': {
        'command': 'cat /etc/os-release | grep PRETTY_NAME | awk -F \'=\' \'{print $2}\' | sed \'s/"//g\'',
        'result': 'CentOS Linux 7 (AltArch)'
    },
    'huge_page_size': {
        'command': "cat /proc/meminfo | grep Hugepagesize | awk -F ':' '{print $2}'",
        'result': '524288 kB'
    },
    'run_level': {
        'command': "who -r | awk '{print $2}'",
        'result': '3'
    },
    'bash': {
        'command': 'bash -version | grep "GNU bash" | awk -F \':\' \'{print $1}\'',
        'result': 'GNU bash, version 4.2.46(2)-release (aarch64-redhat-linux-gnu)'
    },
    'disk': {
        'command': 'lsblk | grep disk | awk \'{print $1":"$4}\'\nls -oQ /dev/disk/by-id/ | grep -v "part" | grep'
                   ' "\\<%s\\"" | grep -v "\\-0x" | awk -F \'"\' \'{print $2}\' | awk -F \'-\' \'{print $1"-"$2}\'',
        'result': [{'name': 'sda', 'capacity': '100G', 'model': 'scsi-0QEMU_QEMU_HARDDISK_drive'}]
    },
    'system': {
        'command': 'hostname\nuname -o',
        'result': 'localhost.localdomain: GNU/Linux'
    },
    'language': {
        'command': "locale charmap\nlocale | grep LC_COLLATE | awk -F '=' '{print $2}'",
        'result': 'en_US.UTF-8 (map="UTF-8", collate="en_US.UTF-8")'
    },
    'transparent_huge_pages': {
        'command': 'cat /sys/kernel/mm/transparent_hugepage/enabled',
        'result': 'always'
    },
    'file_system': {
        'command': 'df -Th ./ | tail -n 1 | awk \'{print $1" "$2}\'',
        'result': 'xfs'
    },
    'mount_param': {
        'command': "mount | grep /dev/sda5 | awk '{print $6}'",
        'result': 'rw,relatime,seclabel,attr2,inode64,noquota'
    },
    'os_disk': {
        'command': '',
        'result': 'sda'
    },
    'io_scheduler': {
        'command': 'cat /sys/block/%s/queue/scheduler',
        'result': 'mq-deadline'
    },
    'binutils': {
        'command': 'ld -v',
        'result': '2.27-34.base.el7'
    }
}

SW_KEY = ['bash', 'binutils', 'file_system', 'gcc', 'glibc', 'gxx', 'huge_page_size', 'io_scheduler', 'java',
          'kernel', 'mount_param', 'os', 'page_size', 'python3', 'run_level', 'software_others',
          'transparent_huge_pages']

HW_KEY = ['arch', 'bios_version', 'cpu_bogomips', 'cpu_cores', 'cpu_logic_cores', 'cpu_max_mhz', 'cpu_model',
          'cpu_nominal_mhz', 'disk', 'display_cards', 'hardware_others', 'l1d_cache', 'l1i_cache', 'l2_cache',
          'l3_cache', 'manufacturer', 'memory_capacity', 'memory_count', 'memory_manu', 'memory_speed',
          'memory_total', 'memory_type', 'netcards', 'os_disk', 'raid', 'product_model', 'sockets']

BM_SUMMARY = ['config', 'copies', 'iterations', 'machine_info', 'mode', 'multi_thread', 'output_dir', 'run_time',
              'score', 'skip_fail', 'test_time', 'tester_name']


class JsonMakerTestCase(unittest.TestCase):
    """
    Test the generation of json file.
    """
    @classmethod
    def setUpClass(cls):
        cls.run_time = time.time()
        cls.cur_dir = os.path.dirname(os.path.abspath(__file__))
        cls.output_dir = os.path.join(cls.cur_dir, "output_dir")
        cls.data_dir = os.path.join(cls.cur_dir, "json_make_data")
        config_file = os.path.join(cls.cur_dir, "../config", "config_sample.ini")
        config_opts = ConfigFileParser().parse_ini(config_file)
        cls.option = OptionCombiner().combine_options(config_opts, {})

    def setUp(self):
        """
        Prepare the test environment. The data directory generated by the test cases is json_make_data,
        and set the temporary output_dir directory to the unittest directory.
        """
        if os.path.exists(self.data_dir):
            shutil.rmtree(self.data_dir)
        os.makedirs(self.data_dir)
        if os.path.exists(self.output_dir):
            shutil.rmtree(self.output_dir)
        os.makedirs(self.output_dir)

    def tearDown(self):
        """
        Delete the data directory generated by test cases.
        """
        if os.path.exists(self.data_dir):
            shutil.rmtree(self.data_dir)
        if os.path.exists(self.output_dir):
            shutil.rmtree(self.output_dir)

    def test_abnormal_params(self):
        """
        Test the scenario where input parameters are abnormal.
        """
        option = {}
        result = {}
        sys_info = {}

        json_make = JsonMaker(option, result, sys_info, self.run_time)
        res = json_make.make_json_file()
        self.assertEqual({}, res)

    def test_file_name(self):
        """
        The name of the generated file meets the expectation.
        """
        json_make = JsonMaker(self.option, {}, {}, self.run_time)
        file_name = json_make.get_json_file()
        pattern = r"\w+_\d.*[0-9]+.json"
        res = re.search(pattern, file_name)
        self.assertIsNotNone(res)

    def test_encode_data(self):
        """
        To test the data encryption process..
        """
        json_make = JsonMaker({}, {}, {}, self.run_time)
        json_make.data_dict[Key.SYSTEM] = {}
        machine_info = deepcopy(MACHINE_INFO)
        self.assertIsNotNone(json_make.get_encode_data(machine_info))

    def test_write_system_info_001(self):
        """
        To test whether the write_machine_info method can correctly process the abnormal option key value.
        """
        machine_info = deepcopy(MACHINE_INFO)
        json_make = JsonMaker(self.option, {}, machine_info, self.run_time)
        json_make.data_dict[Key.SYSTEM] = {}
        self.assertRaises(TypeError, json_make.write_machine_info())

    def test_write_machine_info_002(self):
        """
        To test whether the write method execution result meets the expectation in normal cases.
        """
        machine_info = deepcopy(MACHINE_INFO)
        json_make = JsonMaker(self.option, {}, machine_info, self.run_time)
        json_make.data_dict[Key.SYSTEM] = {}
        sys_dict = json_make.data_dict[Key.SYSTEM]
        sys_dict[Key.HARDWARE] = {}
        sys_dict[Key.SOFTWARE] = {}
        json_make.write_machine_info()
        hardware_key = sorted(list(sys_dict[Key.HARDWARE].keys()))
        software_key = sorted(list(sys_dict[Key.SOFTWARE].keys()))
        self.assertEqual(sorted(SW_KEY), software_key)
        self.assertEqual(sorted(HW_KEY), hardware_key)

    def test_write_bm_summary(self):
        """
        Test whether the execution result of the write_bm_summary method meets the expectation.
        """
        json_make = JsonMaker(self.option, {}, {}, self.run_time)
        json_make.data_dict[Key.BM_CONTENT] = {}
        json_make.write_bench_summary()
        bm_summary = sorted(list(json_make.data_dict[Key.BM_CONTENT][Key.BM_SUMMARY].keys()))
        self.assertEqual(sorted(BM_SUMMARY), bm_summary)

    def test_json_make(self):
        """
        Test whether the result meets the expectation with normal input.
        """
        machine_info = deepcopy(MACHINE_INFO)
        json_make = JsonMaker(self.option, {}, machine_info, self.run_time)
        res = json_make.make_json_file()
        self.assertIsNotNone(res)
        flag = False
        if os.path.exists(json_make.json_file) and os.path.getsize(json_make.json_file) > 0:
            flag = True
        self.assertTrue(flag)


if __name__ == "__main__":
    suite = unittest.TestLoader().loadTestsFromTestCase(JsonMakerTestCase)
    unittest.TextTestRunner(verbosity=2).run(suite)
